package com.paishen.peiniwan.frame.widget;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;

import java.util.Stack;

public class PagerLayout_b extends FrameLayout {
    public interface OnStateChangedListener {
        void upToCurrent();

        void backToLast();

        void touchMove(float progress);
    }

    private OnStateChangedListener callback;

    public void setOnStateChangedListener(OnStateChangedListener callback) {
        this.callback = callback;
    }

    private int mWidth;

    private final Stack<FrameLayout> pageContainers = new Stack<>();
    private FrameLayout layCurrPageWrap;
    private FrameLayout layLastPageWrap;

    private float downX;
    private float downCurrPageTransX, downLastPageTransX;

    private float interceptDownX;
    private float touchSclop;
    private float backTouchWidth;

    private boolean isAnim;
    private boolean touchMove;
    private boolean slidingPage;
    private float tmpMoveProgress; // 0 ~ 1
    private boolean canTouchable = true;

    public PagerLayout_b(Context context) {
        this(context, null);
    }

    public PagerLayout_b(Context context, AttributeSet attrs) {
        super(context, attrs);
        touchSclop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    public void pushNewContent(View view) {
        pushNewContent(view, true);
    }

    public void pushNewContent(View view, boolean anim) {
        final FrameLayout layContentWrap = new FrameLayout(getContext());
        layContentWrap.addView(view);

        View lastWrap = null;
        if (!pageContainers.isEmpty()) {
            lastWrap = pageContainers.peek();
        }

        final View mLastContentWrap = lastWrap;
        if (anim) {
            ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(300);
            animator.addUpdateListener(new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (Float) animation.getAnimatedValue();
                    if (mLastContentWrap != null) {
                        mLastContentWrap.setTranslationX(-(mWidth / 2) * value);
                    }
                    layContentWrap.setTranslationX(getWidth() * (1 - value));
                }
            });
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    isAnim = false;
                    if (mLastContentWrap != null) {
                        mLastContentWrap.setVisibility(View.GONE);
                    }
                }
            });
            animator.start();
            isAnim = true;
        } else {
            if (mLastContentWrap != null) {
                mLastContentWrap.setVisibility(View.GONE);
                final float value = 1;
                mLastContentWrap.setTranslationX(-(mWidth / 2) * value);
            }
        }

        pageContainers.add(layContentWrap);
        addView(layContentWrap);
    }

    public View popToLastContent() {
        return popToLastContent(true);
    }

    public View popToLastContent(boolean anim) {
        if (pageContainers.size() < 2) {
            // Not enough item
            return null;
        }
        final FrameLayout currContentWrap = pageContainers.pop();
        final float currContentWrapTranslateX = currContentWrap.getTranslationX();
        final FrameLayout lastContentWrap = pageContainers.peek();
        final float lastContentWrapTranslateX = lastContentWrap.getTranslationX();

        if (anim) {
            ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(300);
            animator.addUpdateListener(new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (Float) animation.getAnimatedValue();
                    currContentWrap.setTranslationX(currContentWrapTranslateX + (mWidth - currContentWrapTranslateX) * value);
                    lastContentWrap.setTranslationX(lastContentWrapTranslateX * (1 - value));
                }
            });
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    isAnim = false;
                    removeView(currContentWrap);
                }
            });
            animator.start();
            isAnim = true;
            lastContentWrap.setVisibility(View.VISIBLE);
        } else {
            removeView(currContentWrap);
            lastContentWrap.setVisibility(View.VISIBLE);
        }

        if (callback != null) callback.backToLast();
        return lastContentWrap;
    }

    public void returnToCurrentContent(float progress) {
        if (pageContainers.size() < 2) {
            // Not enough item
            return;
        }
        ValueAnimator animator = ValueAnimator.ofFloat(progress, 0).setDuration(300);
        animator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                final float progress = (Float) animation.getAnimatedValue();
                setToLastAnimationProgress(progress);
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                pageContainers.get(pageContainers.size() - 2).setVisibility(View.GONE);
                isAnim = false;
            }
        });
        animator.start();
        isAnim = true;

        if (callback != null) callback.upToCurrent();
    }

    private void setToLastAnimationProgress(float progress) {
        if (pageContainers.size() < 2) {
            // Not enough item
            return;
        }
        final FrameLayout curr = pageContainers.get(pageContainers.size() - 1);
        final FrameLayout last = pageContainers.get(pageContainers.size() - 2);
        last.setVisibility(View.VISIBLE);

        curr.setTranslationX(mWidth * progress);
        last.setTranslationX(-mWidth / 2 * (1 - progress));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        backTouchWidth = mWidth / 5;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                interceptDownX = ev.getRawX();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!isOnlyOnePageOrEmpty() && interceptDownX < backTouchWidth && Math.abs(ev.getRawX() - interceptDownX) > touchSclop) {
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        if (!isAnim && canTouchable) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    return true;
                case MotionEvent.ACTION_MOVE:
                    if (!touchMove && !isOnlyOnePageOrEmpty()) {
                        touchMove = true;
                        downX = event.getRawX();
                        layCurrPageWrap = pageContainers.get(pageContainers.size() - 1);
                        layLastPageWrap = pageContainers.get(pageContainers.size() - 2);
                        downCurrPageTransX = layCurrPageWrap.getTranslationX();
                        downLastPageTransX = layLastPageWrap.getTranslationX();
                    }

                    if (layLastPageWrap != null && layLastPageWrap.getVisibility() != View.VISIBLE) {
                        layLastPageWrap.setVisibility(View.VISIBLE);
                    }

                    if (!slidingPage) {
                        if (!isOnlyOnePageOrEmpty() && downX < backTouchWidth && Math.abs(event.getRawX() - downX) > touchSclop) {
                            slidingPage = true;
                        }
                    } else {
                        float currPageResult = downCurrPageTransX + event.getRawX() - downX;
                        if (currPageResult < 0) {
                            currPageResult = 0;
                        }
                        layCurrPageWrap.setTranslationX(currPageResult);
                        float lastResult = downLastPageTransX + (event.getRawX() - downX) / 2;
                        if (lastResult > 0) {
                            lastResult = 0;
                        }
                        layLastPageWrap.setTranslationX(lastResult);
                        tmpMoveProgress = currPageResult / mWidth;

                        if (callback != null) callback.touchMove(tmpMoveProgress);
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    if (!isOnlyOnePageOrEmpty()) {
                        if (slidingPage) {
                            if (event.getRawX() - downX > backTouchWidth) {
                                popToLastContent();
                                tmpMoveProgress = 1;
                            } else {
                                returnToCurrentContent(tmpMoveProgress);
                                tmpMoveProgress = 0;
                            }
                        } else {
                            if (layLastPageWrap != null && layLastPageWrap.getVisibility() != View.GONE) {
                                layLastPageWrap.setVisibility(View.GONE);
                            }
                        }
                    }
                    touchMove = false;
                    slidingPage = false;
                    break;
            }
        }
        return super.onTouchEvent(event);
    }

    public boolean isOnlyOnePageOrEmpty() {
        return pageContainers.size() <= 1;
    }

    public boolean isDoingAnim() {
        return isAnim;
    }

    public void setCanTouchable(boolean isCanTouchable) {
        canTouchable = isCanTouchable;
    }
}
